package net.zjitc.yaobei_backed.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import net.zjitc.yaobei_backed.dto.StudyStats;
import net.zjitc.yaobei_backed.dto.study.StudyResultRequest;
import net.zjitc.yaobei_backed.dto.study.StudyResultResponse;
import net.zjitc.yaobei_backed.entity.StudyRecord;
import net.zjitc.yaobei_backed.entity.StudyTask;
import net.zjitc.yaobei_backed.entity.UserProgress;
import net.zjitc.yaobei_backed.repository.StudyRecordRepository;
import net.zjitc.yaobei_backed.repository.StudyTaskRepository;
import net.zjitc.yaobei_backed.repository.UserProgressRepository;
import net.zjitc.yaobei_backed.service.StudyService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Objects;

/**
 * 学习统计服务实现
 */
@Service
public class StudyServiceImpl implements StudyService {

    private static final float DEFAULT_FORGET_FACTOR = 0.8f;
    private static final float MIN_FORGET_FACTOR = 0.5f;
    private static final float MAX_FORGET_FACTOR = 0.95f;

    @Autowired
    private StudyRecordRepository studyRecordRepository;

    @Autowired
    private UserProgressRepository userProgressRepository;

    @Autowired
    private StudyTaskRepository studyTaskRepository;

    @Override
    public StudyStats getStudyStats(Integer userId) {
        // 1. 计算用户剩余需要学习词书中的总单词数
        // 逻辑：user_progress.total_words - user_progress.learned_words
        QueryWrapper<UserProgress> progressWrapper = new QueryWrapper<>();
        progressWrapper.eq("user_id", userId);
        UserProgress progress = userProgressRepository.selectOne(progressWrapper);

        Integer remainingWordsToLearn = 0;
        if (progress != null && progress.getTotalWords() != null && progress.getLearnedWords() != null) {
            remainingWordsToLearn = progress.getTotalWords() - progress.getLearnedWords();
            // 确保结果不为负数
            if (remainingWordsToLearn < 0) {
                remainingWordsToLearn = 0;
            }
        }

        // 2. 计算用户今天需要复习的单词数
        // 逻辑：COUNT(*) FROM study_record WHERE user_id=? AND DATE(next_review_at)=CURDATE()
        QueryWrapper<StudyRecord> todayReviewWrapper = new QueryWrapper<>();
        todayReviewWrapper.eq("user_id", userId);
        todayReviewWrapper.apply("DATE(next_review_at) = CURDATE()");
        Long todayReviewCountLong = studyRecordRepository.selectCount(todayReviewWrapper);
        Integer todayReviewCount = todayReviewCountLong.intValue();

        return new StudyStats(remainingWordsToLearn, todayReviewCount);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public StudyResultResponse saveStudyResult(Integer userId, StudyResultRequest request) {
        if (request == null) {
            throw new IllegalArgumentException("请求体不能为空");
        }
        Integer wordId = request.getWordId();
        if (wordId == null) {
            throw new IllegalArgumentException("wordId不能为空");
        }

        float recallConfidence = clampConfidence(request.getRecallConfidence());
        String confidenceType = normalizeConfidence(request.getConfidence());
        String mode = normalizeMode(request.getMode());
        LocalDateTime now = LocalDateTime.now();

        StudyRecord record = studyRecordRepository.selectOne(new QueryWrapper<StudyRecord>()
                .eq("user_id", userId)
                .eq("word_id", wordId));
        boolean isNewRecord = (record == null);

        if (isNewRecord) {
            record = new StudyRecord();
            record.setUserId(userId);
            record.setWordId(wordId);
            record.setReviewCount(0);
            record.setForgetFactor(DEFAULT_FORGET_FACTOR);
        }

        int previousReviewCount = record.getReviewCount() == null ? 0 : record.getReviewCount();
        float forgetFactor = record.getForgetFactor() == null ? DEFAULT_FORGET_FACTOR : record.getForgetFactor();

        forgetFactor = adjustForgetFactor(forgetFactor, confidenceType);
        int reviewCount = updateReviewCount(previousReviewCount, confidenceType, isNewRecord);
        int intervalDays = calculateIntervalDays(reviewCount, recallConfidence, forgetFactor, confidenceType);
        LocalDateTime nextReviewAt = now.plusDays(intervalDays);

        record.setRecallConfidence(recallConfidence);
        record.setForgetFactor(forgetFactor);
        record.setReviewCount(reviewCount);
        record.setLastReviewAt(now);
        record.setNextReviewAt(nextReviewAt);

        if (isNewRecord) {
            studyRecordRepository.insert(record);
            touchUserProgressForNewWord(userId, now);
            touchTodayStudyTask(userId);
        } else {
            studyRecordRepository.updateById(record);
        }

        return new StudyResultResponse(
                wordId,
                mode,
                reviewCount,
                recallConfidence,
                forgetFactor,
                now,
                nextReviewAt,
                intervalDays
        );
    }

    private float clampConfidence(Float value) {
        if (value == null) {
            return 0.5f;
        }
        return Math.max(0f, Math.min(1f, value));
    }

    private String normalizeConfidence(String confidence) {
        if (confidence == null) {
            return "blur";
        }
        switch (confidence.toLowerCase()) {
            case "know":
            case "forget":
            case "blur":
                return confidence.toLowerCase();
            default:
                return "blur";
        }
    }

    private String normalizeMode(String mode) {
        if (mode == null) {
            return "review";
        }
        switch (mode.toLowerCase()) {
            case "learn":
            case "review":
                return mode.toLowerCase();
            default:
                return "review";
        }
    }

    private float adjustForgetFactor(float currentFactor, String confidenceType) {
        float updated = currentFactor;
        switch (confidenceType) {
            case "know":
                updated += 0.05f;
                break;
            case "blur":
                updated -= 0.02f;
                break;
            case "forget":
                updated -= 0.08f;
                break;
            default:
                break;
        }
        if (updated < MIN_FORGET_FACTOR) {
            updated = MIN_FORGET_FACTOR;
        }
        if (updated > MAX_FORGET_FACTOR) {
            updated = MAX_FORGET_FACTOR;
        }
        return updated;
    }

    private int updateReviewCount(int previousCount, String confidenceType, boolean isNewRecord) {
        if (isNewRecord) {
            return previousCount + 1;
        }
        if (Objects.equals(confidenceType, "forget")) {
            return Math.max(1, previousCount);
        }
        return previousCount + 1;
    }

    /**
     * 根据艾宾浩斯记忆曲线计算复习间隔天数
     * 标准艾宾浩斯曲线复习间隔：
     * - 第1次复习：1天
     * - 第2次复习：2天
     * - 第3次复习：4天
     * - 第4次复习：7天
     * - 第5次复习：15天
     * - 第6次复习：30天
     * - 之后：按指数增长
     * 
     * 同时考虑记忆置信度和遗忘系数进行动态调整
     */
    private int calculateIntervalDays(int reviewCount, float recallConfidence, float forgetFactor, String confidenceType) {
        // 如果答错了，强制1天后复习
        if (Objects.equals(confidenceType, "forget")) {
            return 1;
        }
        
        // 标准艾宾浩斯曲线复习间隔表
        int[] ebbinghausIntervals = {1, 2, 4, 7, 15, 30};
        
        // 如果复习次数在标准间隔表范围内，直接使用
        if (reviewCount > 0 && reviewCount <= ebbinghausIntervals.length) {
            int baseInterval = ebbinghausIntervals[reviewCount - 1];
            
            // 根据记忆置信度调整（0.5~1.0，映射到0.8~1.2倍）
            double confidenceMultiplier = 0.8 + (recallConfidence - 0.5) * 0.8; // 0.5->0.8, 1.0->1.2
            
            // 根据遗忘系数调整（0.5~0.95，映射到0.7~1.3倍）
            double forgetMultiplier = 0.7 + (forgetFactor - MIN_FORGET_FACTOR) / (MAX_FORGET_FACTOR - MIN_FORGET_FACTOR) * 0.6;
            
            // 计算调整后的间隔
            double adjustedInterval = baseInterval * confidenceMultiplier * forgetMultiplier;
            return Math.max(1, (int) Math.round(adjustedInterval));
        }
        
        // 超过标准间隔表后，使用指数增长（基于第6次的30天）
        // 公式：30 * 1.5^(reviewCount - 6)
        double baseInterval = 30.0;
        double growthFactor = Math.pow(1.5, reviewCount - 6);
        
        // 根据记忆置信度和遗忘系数调整
        double confidenceMultiplier = 0.8 + (recallConfidence - 0.5) * 0.8;
        double forgetMultiplier = 0.7 + (forgetFactor - MIN_FORGET_FACTOR) / (MAX_FORGET_FACTOR - MIN_FORGET_FACTOR) * 0.6;
        
        double adjustedInterval = baseInterval * growthFactor * confidenceMultiplier * forgetMultiplier;
        
        // 限制最大间隔为365天（1年）
        return Math.min(365, Math.max(1, (int) Math.round(adjustedInterval)));
    }

    private void touchUserProgressForNewWord(Integer userId, LocalDateTime now) {
        QueryWrapper<UserProgress> wrapper = new QueryWrapper<>();
        wrapper.eq("user_id", userId);
        UserProgress progress = userProgressRepository.selectOne(wrapper);
        if (progress == null) {
            return;
        }
        Integer learned = progress.getLearnedWords() == null ? 0 : progress.getLearnedWords();
        progress.setLearnedWords(learned + 1);
        progress.setLastStudy(now);
        userProgressRepository.updateById(progress);
    }

    private void touchTodayStudyTask(Integer userId) {
        QueryWrapper<StudyTask> wrapper = new QueryWrapper<>();
        wrapper.eq("user_id", userId);
        wrapper.eq("task_date", LocalDate.now());
        StudyTask task = studyTaskRepository.selectOne(wrapper);
        if (task == null) {
            return;
        }
        Integer finished = task.getFinishedWords() == null ? 0 : task.getFinishedWords();
        task.setFinishedWords(finished + 1);
        if (task.getTargetWords() != null && task.getFinishedWords() >= task.getTargetWords()) {
            task.setStatus("completed");
        }
        studyTaskRepository.updateById(task);
    }
}
